package com.letu.core.utils;

import com.letu.core.exception.ResponseCodeException;
import com.google.common.io.ByteStreams;
import sun.misc.BASE64Encoder;

import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateExpiredException;
import java.security.cert.CertificateNotYetValidException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Date;
import java.util.Map;


public class HttpClientUtils {
    private static SSLSocketFactory sslSocketFactory;
    private static final String NEW_LINE = "\r\n";
    private static final String BOUNDARY_PREFIX = "--";
    private static final String BOUNDARY = "========7d4a6d158c9";
    private static final String CONTENT_TYPE = "Content-Type:application/octet-stream";
    private static final String CONTENT_DISPOSITION = "Content-Disposition: form-data;name=\"photo\";filename=\"" + "" + "\"";

    private static final String FILE_HEADER = BOUNDARY_PREFIX
            + BOUNDARY
            + NEW_LINE
            + CONTENT_DISPOSITION
            + NEW_LINE
            + CONTENT_TYPE
            + NEW_LINE
            + NEW_LINE;

    private static final String FILE_END = NEW_LINE
            + BOUNDARY_PREFIX
            + BOUNDARY
            + BOUNDARY_PREFIX
            + NEW_LINE;

    private static final String FORM_END = BOUNDARY_PREFIX
            + BOUNDARY
            + NEW_LINE;


    public static void postFile(URL url, Map<String, String> properties, byte[] data, OutputStream out) throws IOException {
        String result = "";
        // 定义数据分隔线
        HttpURLConnection conn = null;
        try { // 换行符
            conn = openConnection(url, null, null);
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            conn.setDoInput(true);
            conn.setRequestProperty("connection", "Keep-Alive");
            conn.setRequestProperty("Charset", "UTF-8");
            conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
            OutputStream pout = new DataOutputStream(conn.getOutputStream());
            if (properties != null) {
                StringBuffer param = new StringBuffer();
                for (Map.Entry<String, String> entry : properties.entrySet()) {
                    param.append(FORM_END);
                    param.append("Content-Disposition: form-data; name=\"").append(entry.getKey()).append("\"\r\n\r\n");
                    param.append(entry.getValue());
                    param.append("\r\n");
                }
                pout.write(param.toString().getBytes());
            }
            pout.write(FILE_HEADER.getBytes());
            pout.write(data);
            byte[] end_data = FILE_END.getBytes();
            pout.write(end_data);
            pout.flush();
            pout.close();
            InputStream in = conn.getInputStream();
            ByteStreams.copy(in, out);
        } catch (IOException e) {
            throw e;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static void postFile(URL url, byte[] data, OutputStream out) throws IOException {
        postFile(url, null, data, out);
    }

    /**
     * @param url
     * @param data StringBuffer params = new StringBuffer();
     *             params.append("wen").append("=").append(wen).append("&")
     *             .append("btnSearch").append("=").append(btnSearch);
     *             byte[] bypes = params.toString().getBytes();
     * @param out  ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
     * @throws IOException
     */

    public static void post(URL url, byte[] data, OutputStream out) throws IOException {
        post(url, null, null, null, data, out);
    }

    public static void post(URL url, Map<String, String> properties, byte[] data, OutputStream out) throws IOException {
        post(url, null, null, properties, data, out);
    }

    public static void post(URL url, String userName, String password, Map<String, String> properties,
                            byte[] data, OutputStream out) throws IOException {
        HttpURLConnection conn = null;
        try {
            conn = openConnection(url, userName, password);
            conn.setRequestMethod("POST");
            conn.setDoOutput(true);
            if (properties != null) {
                for (String key : properties.keySet()) {
                    conn.setRequestProperty(key, properties.get(key));
                }
            }
            OutputStream pout = conn.getOutputStream();
            pout.write(data);
            pout.close();
            int responseCode = conn.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new IOException("HTTP Error Response: " + responseCode);
            }
            InputStream in = conn.getInputStream();
            ByteStreams.copy(in, out);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static byte[] get(URL url) throws IOException {
        HttpURLConnection conn = null;
        InputStream in = null;
        byte[] bytes;
        try {
            conn = openConnection(url, null, null);
            conn.setRequestMethod("GET");
            int responseCode = conn.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new IOException("HTTP Error Response: " + responseCode);
            }
            in = conn.getInputStream();

            bytes = ByteStreams.toByteArray(in);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
        return bytes;
    }

    public static void get(URL url, OutputStream out) throws IOException {
        get(url, null, null, out);
    }

    public static void get(URL url, String userName, String password, OutputStream out) throws IOException {
        HttpURLConnection conn = null;
        try {
            conn = openConnection(url, userName, password);
            conn.setRequestMethod("GET");
            int responseCode = conn.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new IOException("HTTP Error Response: " + responseCode);
            }
            InputStream in = conn.getInputStream();
            ByteStreams.copy(in, out);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static void get(URL url, Map<String, String> properties, OutputStream out) throws IOException {
        HttpURLConnection conn = null;
        try {
            conn = openConnection(url, null, null);
            conn.setRequestMethod("GET");
            if (properties != null) {
                for (String key : properties.keySet()) {
                    conn.setRequestProperty(key, properties.get(key));
                }
            }
            int responseCode = conn.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                throw new IOException("HTTP Error Response: " + responseCode);
            }
            InputStream in = conn.getInputStream();
            ByteStreams.copy(in, out);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static void getAlwaysReturn(URL url, Map<String, String> properties, OutputStream out) throws IOException {
        HttpURLConnection conn = null;
        try {
            conn = openConnection(url, null, null);
            conn.setRequestMethod("GET");
            if (properties != null) {
                for (String key : properties.keySet()) {
                    conn.setRequestProperty(key, properties.get(key));
                }
            }
            int responseCode = conn.getResponseCode();
            if (responseCode != HttpURLConnection.HTTP_OK) {
                InputStream in = conn.getErrorStream();
                ByteStreams.copy(in, out);
                throw new ResponseCodeException(String.valueOf(responseCode));
            }
            InputStream in = conn.getInputStream();
            ByteStreams.copy(in, out);
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static boolean isOk(String url) {
        HttpURLConnection conn = null;
        try {
            conn = openConnection(new URL(url), null, null);
            conn.setRequestMethod("GET");
            int responseCode = conn.getResponseCode();
            return responseCode == HttpURLConnection.HTTP_OK;
        } catch (Exception e) {
            return false;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    public static boolean check(URL url, Date ifms) throws IOException {
        HttpURLConnection conn = null;
        try {
            conn = openConnection(url, null, null);
            conn.setRequestMethod("GET");
            conn.setIfModifiedSince(ifms == null ? 0 : ifms.getTime());
            int responseCode = conn.getResponseCode();
            return responseCode == HttpURLConnection.HTTP_OK;
        } finally {
            if (conn != null) {
                conn.disconnect();
            }
        }
    }

    /**
     * 下载图片,并转成base64
     *
     * @param imgURL
     * @return
     */
    @Deprecated
    public static String readInfoStreamToBytes(String imgURL) {
        if (imgURL == null) {
            return null;
        }
        InputStream inStream = null;
        // 字节数组
        byte[] bCache = new byte[2048];
        int readSize = 0;// 每次读取的字节长度
        int totalSize = 0;// 总字节长度
        ByteArrayOutputStream infoStream = new ByteArrayOutputStream();
        try {
            // 创建URL
            URL url = new URL(imgURL);
            // 创建链接
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(5 * 1000);
            inStream = conn.getInputStream();
            // 一次性读取2048字节
            while ((readSize = inStream.read(bCache)) > 0) {
                totalSize += readSize;
//                if (totalSize > PROTECTED_LENGTH) {
//                    logger.error("输入流超出5M大小限制");
//                    return null;
//                }
                // 将bcache中读取的input数据写入infoStream
                infoStream.write(bCache, 0, readSize);
            }
        } catch (IOException e1) {
//            logger.error("输入流读取异常");
            return null;
        } finally {
            try {
                inStream.close();
            } catch (IOException e) {
//                logger.error("输入流关闭异常");
            }
        }
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(infoStream.toByteArray());
    }

    public static HttpURLConnection openConnection(URL url, String userName, String password) throws
            IOException {
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        if (conn instanceof HttpsURLConnection) {
            ((HttpsURLConnection) conn).setSSLSocketFactory(getSSLSocketFactory());
            ((HttpsURLConnection) conn).setHostnameVerifier(new HttpsHostnameVerifier());
        }
        conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)");
        conn.setRequestProperty("Authorization", "Basic " + encodeBasic(userName, password));
        if (userName != null && password != null) {
            conn.setRequestProperty("Authorization", "Basic " + encodeBasic(userName, password));
        }
        String proxyUserName = System.getProperty("http.proxyUserName");
        String proxyPassword = System.getProperty("http.proxyPassword");
        if (proxyUserName != null && proxyUserName.length() > 0) {
            conn.setRequestProperty("Proxy-Authorization", "Basic " + encodeBasic(proxyUserName, proxyPassword));
        }
        conn.setConnectTimeout(300 * 1000);
        conn.setReadTimeout(300 * 1000);
        conn.setUseCaches(false);
        return conn;
    }

    private static String encodeBasic(String userName, String password) {
        String userPass = userName + ":" + password;
        Base64.Encoder encoder = Base64.getEncoder();
        return new String(encoder.encode(userPass.getBytes()));
    }

    private static SSLSocketFactory getSSLSocketFactory() {
        if (sslSocketFactory == null) {
            try {
                SSLContext context = SSLContext.getInstance("TLS");
                context.init(null, new TrustManager[]{new HttpsTrustManager()}, new java.security.SecureRandom());
                sslSocketFactory = context.getSocketFactory();
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            } catch (KeyManagementException e) {
                e.printStackTrace();
            }
        }
        return sslSocketFactory;
    }

    private static class HttpsTrustManager implements X509TrustManager {
        public boolean isClientTrusted(X509Certificate[] certificates) {
            return true;
        }

        public boolean isServerTrusted(X509Certificate[] certificates) {
            try {
                certificates[0].checkValidity();
                return true;
            } catch (CertificateExpiredException e) {
                return false;
            } catch (CertificateNotYetValidException e) {
                return false;
            }
        }

        @Override
        public void checkClientTrusted(X509Certificate[] certificates, String s) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] certificates, String s) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

    private static class HttpsHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String s, SSLSession sslSession) {
            return true;
        }
    }
}

